package com.example.cybz.common.exception;


import com.example.cybz.common.result.Result;
import com.example.cybz.common.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 全局异常处理器
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Value("${spring.profiles.active:dev}")
    private String profile;

    @Value("${spring.application.name:unknown}")
    private String applicationName;

    // 获取当前请求
    private HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    // 获取请求ID
    private String getTraceId() {
        return (String) getRequest().getAttribute("traceId");
    }

    // 记录异常日志
    private void logException(Exception e, String message) {
        if (log.isDebugEnabled()) {
            log.debug("{} [TraceID: {}] - {}", message, getTraceId(), e.getMessage(), e);
        } else {
            log.error("{} [TraceID: {}] - {}", message, getTraceId(), e.getMessage());
        }
    }

    // 构建错误响应
    private Result<Object> buildErrorResult(ResultCodeEnum codeEnum, String message, Object data) {
        return Result.fail(codeEnum, message)
                .addMetadata("traceId", getTraceId())
                .addMetadata("service", applicationName)
                .setData(data);
    }

    /* ====================== 业务异常处理 ====================== */

    /**
     * 自定义业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Object> handleBusinessException(BusinessException e) {
        logException(e, "业务异常");
        return buildErrorResult(e.getCode(), e.getMessage(), e.getData());
    }

    /* ====================== 参数校验异常处理 ====================== */

    /**
     * 方法参数校验异常（@Validated）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        List<String> errors = fieldErrors.stream()
                .map(fieldError ->
                        fieldError.getField() + ": " + fieldError.getDefaultMessage())
                .collect(Collectors.toList());

        logException(e, "参数校验失败");
        return buildErrorResult(ResultCodeEnum.PARAM_VALIDATION_FAILED,
                "参数校验失败", errors);
    }

    /**
     * 对象绑定异常（@Valid）
     */
    @ExceptionHandler(BindException.class)
    public Result<Object> handleBindException(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> errors = fieldErrors.stream()
                .map(fieldError ->
                        fieldError.getField() + ": " + fieldError.getDefaultMessage())
                .collect(Collectors.toList());

        logException(e, "对象绑定异常");
        return buildErrorResult(ResultCodeEnum.PARAM_BIND_ERROR,
                "对象绑定异常", errors);
    }

    /**
     * 约束违反异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Object> handleConstraintViolation(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        List<String> errors = violations.stream()
                .map(violation ->
                        violation.getPropertyPath() + ": " + violation.getMessage())
                .collect(Collectors.toList());

        logException(e, "约束违反异常");
        return buildErrorResult(ResultCodeEnum.PARAM_VALIDATION_FAILED,
                "约束违反异常", errors);
    }

    /**
     * 请求参数缺失异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<Object> handleMissingParameter(MissingServletRequestParameterException e) {
        logException(e, "请求参数缺失");
        String error = "缺少必要参数: " + e.getParameterName();
        return buildErrorResult(ResultCodeEnum.PARAM_MISSING,
                "请求参数缺失", error);
    }

    /**
     * 参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<Object> handleTypeMismatch(MethodArgumentTypeMismatchException e) {
        logException(e, "参数类型不匹配");
        String error = "参数 '" + e.getName() + "' 应为类型: " +
                (e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "未知");
        return buildErrorResult(ResultCodeEnum.PARAM_TYPE_MISMATCH,
                "参数类型不匹配", error);
    }

    /**
     * JSON解析异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException e) {
        logException(e, "JSON解析异常");
        return buildErrorResult(ResultCodeEnum.REQUEST_BODY_INVALID,
                "JSON解析异常", "请求体格式错误");
    }

    /* ====================== HTTP请求异常处理 ====================== */

    /**
     * 请求方法不支持异常
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<Object> handleMethodNotSupported(HttpRequestMethodNotSupportedException e) {
        logException(e, "请求方法不支持");
        return buildErrorResult(ResultCodeEnum.METHOD_NOT_ALLOWED,
                "请求方法不支持", e.getMethod());
    }

    /**
     * 媒体类型不支持异常
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<Object> handleMediaTypeNotSupported(HttpMediaTypeNotSupportedException e) {
        logException(e, "媒体类型不支持");
        return buildErrorResult(ResultCodeEnum.UNSUPPORTED_MEDIA_TYPE,
                "媒体类型不支持", e.getContentType());
    }

    /* ====================== 安全认证异常处理 ====================== */

    /**
     * 访问拒绝异常（权限不足）
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(AccessDeniedException.class)
    public Result<Object> handleAccessDenied(AccessDeniedException e) {
        logException(e, "访问拒绝");
        return buildErrorResult(ResultCodeEnum.FORBIDDEN,
                "权限不足", getRequest().getRequestURI());
    }


    /* ====================== 数据库异常处理 ====================== */

    /**
     * SQL异常
     */
    @ExceptionHandler(SQLException.class)
    public Result<Object> handleSqlException(SQLException e) {
        log.error("SQL异常 [TraceID: {}] - {}", getTraceId(), e.getMessage(), e);
        return buildErrorResult(ResultCodeEnum.DATABASE_ERROR,
                "数据库操作异常", "production".equals(profile) ? null : e.getMessage());
    }

    /**
     * 数据库访问异常
     */
    @ExceptionHandler(DataAccessException.class)
    public Result<Object> handleDataAccessException(DataAccessException e) {
        log.error("数据库访问异常 [TraceID: {}] - {}", getTraceId(), e.getMessage(), e);
        return buildErrorResult(ResultCodeEnum.DATABASE_ERROR,
                "数据库访问异常", "production".equals(profile) ? null : e.getCause().getMessage());
    }

    /* ====================== 第三方服务异常处理 ====================== */

    /**
     * 第三方服务异常（自定义）
     */
    @ExceptionHandler(ThirdPartyServiceException.class)
    public Result<Object> handleThirdPartyServiceException(ThirdPartyServiceException e) {
        logException(e, "第三方服务异常");
        return buildErrorResult(ResultCodeEnum.THIRD_PARTY_ERROR,
                e.getMessage(), e.getServiceName());
    }

    /* ====================== 系统级异常处理 ====================== */

    /**
     * 所有未处理的异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public Result<Object> handleGlobalException(Exception e) {
        // 记录完整错误堆栈
        log.error("系统异常 [TraceID: {}] - {}", getTraceId(), e.getMessage(), e);

        // 生产环境不返回详细错误
        String errorMessage = "production".equals(profile)
                ? "系统异常，请联系管理员"
                : e.getMessage();

        // 包含错误堆栈（仅开发环境）
        Object errorDetail = "production".equals(profile)
                ? null
                : buildErrorDetail(e);

        return buildErrorResult(ResultCodeEnum.SYSTEM_ERROR,
                errorMessage, errorDetail);
    }

    // 构建错误详细信息
    private Object buildErrorDetail(Exception e) {
        Map<String, Object> errorDetail = new LinkedHashMap<>();
        errorDetail.put("error", e.getClass().getName());
        errorDetail.put("message", e.getMessage());

        // 开发环境添加堆栈信息
        if ("dev".equals(profile)) {
            List<String> stackTrace = Arrays.stream(e.getStackTrace())
                    .limit(10)
                    .map(StackTraceElement::toString)
                    .collect(Collectors.toList());
            errorDetail.put("stackTrace", stackTrace);
        }

        return errorDetail;
    }

    // ====================== 异常通知机制 ======================

    /**
     * 发送异常通知（可选实现）
     */
    @Async
    public void sendExceptionNotification(Exception e, HttpServletRequest request) {
        // 实现邮件或钉钉通知逻辑
        // 示例：
        String message = String.format(
                "【%s】服务异常通知\n" +
                        "时间: %s\n" +
                        "请求URI: %s\n" +
                        "TraceID: %s\n" +
                        "异常信息: %s\n" +
                        "环境: %s",
                applicationName,
                LocalDateTime.now(),
                request.getRequestURI(),
                getTraceId(),
                e.getMessage(),
                profile
        );

        // 发送通知（实际实现根据企业通知机制）
        // notificationService.sendAlert(message);
    }
}
